package com.project.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.project.dao.GenericDAO;

/**
 * Generic DAO Interface Implement.
 * 
 * @author travisl@synnex.com
 * 
 * @param <E>
 *            Entity
 * @param <P>
 *            Primary Key
 */
public abstract class GenericDAOImpl<E extends Serializable, P extends Serializable>
		implements GenericDAO<E, P> {
	private final Class<E> entityClass;
	private final String HQL_LIST_ALL;

	@Autowired
	@Qualifier("sessionFactory")
	private SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public GenericDAOImpl() {
		this.entityClass = (Class<E>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];

		HQL_LIST_ALL = "from " + this.entityClass.getSimpleName();
	}

	public Session getSession() {

		return sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	@Override
	public P save(E entity) {
		return (P) getSession().save(entity);
	}

	@Override
	public void saveOrUpdate(E entity) {
		getSession().saveOrUpdate(entity);
	}

	@Override
	public void update(E entity) {
		getSession().update(entity);
	}

	@Override
	public void delete(E entity) {
		getSession().delete(entity);
	}

	@SuppressWarnings("unchecked")
	@Override
	public E get(P id) {
		return (E) getSession().get(this.entityClass, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<E> listAll() {
		return (List<E>) getSession().createQuery(HQL_LIST_ALL).list();
	}

	/**
	 * generic method to query a entity list via parameters from database.
	 * 
	 * @param hql
	 * @param objects
	 * @return entity list
	 */
	@SuppressWarnings("unchecked")
	protected List<E> executeQuery(final String hql, final Object... objects) {
		Query query = getSession().createQuery(hql);
		if (objects != null && objects.length > 0) {
			for (int i = 0; i < objects.length; i++) {
				query.setParameter(i, objects[i]);
			}
		}
		return (List<E>) query.list();
	}

	/**
	 * generic method to query a entity via parameters from database.
	 * 
	 * @param hql
	 * @param objects
	 * @return entity
	 */
	@SuppressWarnings("unchecked")
	protected E executeUniqueQuery(final String hql, final Object... objects) {
		Query query = getSession().createQuery(hql);
		if (objects != null && objects.length > 0) {
			for (int i = 0; i < objects.length; i++) {
				query.setParameter(i, objects[i]);
			}
		}
		return (E) query.uniqueResult();
	}

	/**
	 * generic method to query a entity list by page from database.
	 * 
	 * @param hql
	 * @param offset
	 *            first record index
	 * @param pageSize
	 *            page size
	 * @return each record of request page
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByPage(final String hql, final int offset,
			final int pageSize, final Object... objects) {
		Query query = getSession().createQuery(hql);
		if (objects != null && objects.length > 0) {
			for (int i = 0; i < objects.length; i++) {
				query.setParameter(i, objects[i]);
			}
		}
		return (List<E>) query.setFirstResult(offset).setMaxResults(pageSize)
				.list();
	}

}
